package org.wizehack.mri;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.wizehack.mri.CoverageDataParser.CoverageFileReader;
import org.wizehack.mri.CoverageDataParser.SourceFile;
import org.wizehack.mri.code.CodeLoader;
import org.wizehack.mri.code.JavaCodeLoader;
import org.wizehack.mri.fl.SFLAlgorithm;
import org.wizehack.mri.io.FileListReader;
import org.wizehack.mri.repo.MongoDBAccessPoint;
import org.wizehack.mri.test.BasicTestCase;
import org.wizehack.mri.test.BasicTestSuite;
import org.wizehack.mri.test.TestCase;
import org.wizehack.mri.test.TestConfigurator;
import org.wizehack.mri.test.TestSuite;

import com.mongodb.DBCursor;
import com.mongodb.DBObject;

public abstract class SFLocalizer {	

	private ArrayList<TestSuite> tsList;
	private MongoDBAccessPoint dbap;
	
//	private static Logger logger;
	
	public void createProject(String projectName, String srcCodeLocation,
			int numberOfTestSuite) {
//		File logFile = new File("./log/log4j.txt");
//		logFile.delete();
//		PropertyConfigurator.configure("./config/log4j.properties");
//		logger = Logger.getLogger(SFLocalizer.class);
		
		tsList = new ArrayList<TestSuite>();
		dbap = MongoDBAccessPoint.getInstance();
		TestConfigurator.getInstance().setSourceCodeDirLocation(srcCodeLocation);
		setProjectName(projectName);	
		TestConfigurator.getInstance().setNumberOfTestSuite(numberOfTestSuite);
		setSourceCodeLocation(srcCodeLocation);
	}
	
	public abstract void setUp();
	public abstract void registerTestSets();
	
	public TestSuite configureTestSuite(List<String> covFileList, List<String> failedCoverageFileList) {
		TestSuite testSuite = new BasicTestSuite();
		for(int i=0; i < covFileList.size(); i++){
			TestCase testCase = new BasicTestCase();
			testCase.setCoverageLocation(covFileList.get(i));
			
			testCase.setTcId(i);
			
			List<SourceFile> coverage = this.getCoveredLine(testCase);
			testCase.setCoverage(coverage);
			
			if(failedCoverageFileList != null && failedCoverageFileList.contains(testCase.getCoverageLocation()))
				testCase.setPassed(false);		
			else
				testCase.setPassed(true);
			
			testSuite.add(testCase);
		}
		
		return testSuite;
	}
	
	public List<SourceFile> getCoveredLine(TestCase testCase){
		File tcLocaction = new File(testCase.getCoverageLocation());
		CoverageFileReader cfr = new CoverageFileReader(tcLocaction);
		
		List <SourceFile> coverage = cfr.getCoverage();
		
		for(int i=0; i<coverage.size(); i++){
			SourceFile sf = coverage.get(i);
					
			for(int j=0; j<sf.size(); j++){
				if(sf.get(j).isCovered()==false){
					sf.remove(j);
				}
			}
		}
		
		return coverage;
	}
	
	public List<String> scanCoverageFile(String dirPath){
		FileListReader fileListReader = new FileListReader();
		List<String> fList = fileListReader.readFiles(new File(dirPath));
//		for(int i=0; i<fList.size(); i++){
//			System.out.println(fList.get(i));
//		}
		return fList;
	}
	
	public void setNumberOfTestCase(int[] numberOfTestCase){
		TestConfigurator.getInstance().setNumberOfTestCase(numberOfTestCase);
	}
	
	public void addTestSuite(TestSuite ts) {
		tsList.add(ts);
		
//		for(int i=0; i<tsList.size(); i++){
//			for(int j=0; j<tsList.get(i).size(); j++){
//				TestCase tc = tsList.get(i).get(j);
//				if(tc.getCoverageLocation().indexOf("/13.xml")>0){
//					System.out.println("tc: " + j + ", isPassed: " + tc.isPassed());
//					List<SourceFile> source = tc.getCoverage();
//					for(int k=0; k<source.size(); k++){
//						
//						if(source.get(k).getName().indexOf("EditorActions.java")>0){
//							
//							for(int l=0; l<source.get(k).size(); l++){
//								int line = source.get(k).get(l).getLineNumber();
//								if(line > 450 && line < 480){
//									System.out.println(line);
//								}
//							}
//						}
//						
//					}
//				}
//			}
//		}
		
	}

	public void probe() {
		setUp();
		registerTestSets();
		setTestResult();
		setCoverage();
	}
		
	public void debug(SFLAlgorithm algorithm){
//		logger.info("Calculating suspiciousness.....");
		System.out.print("Calculating suspiciousness.....");
		TestConfigurator tConf = TestConfigurator.getInstance();

		int[] totalPassed = getTotalPassed(tConf.getNumberOfTestSuite());
		int[] totalFailed = getTotalFailed(totalPassed);
		
		int noTestSuite = tConf.getNumberOfTestSuite();
		int[] noTestCase = tConf.getNumberOfTestCase();
		
		boolean[][] isPassed = getTestResult(tConf.getNumberOfTestSuite(), noTestCase);
		List<Object> sourceIdList = dbap.getDBObjectListFromSourceCode();
		
		for(int i=0; i<sourceIdList.size(); i++){
			String sourceCodeId = sourceIdList.get(i).toString();
//			String code = sourceIdList.get(i).get("code").toString();
//			int number = (int)sourceIdList.get(i).get("number");
			calculateSuspiciousness(algorithm, totalPassed, totalFailed,
					noTestSuite, isPassed, sourceCodeId);
		} //end for i	
		
//		logger.info("ok");
		System.out.println("ok");
		endDebug();
	}
	
	

	private synchronized void calculateSuspiciousness(SFLAlgorithm algorithm,
			int[] totalPassed, int[] totalFailed, int noTestSuite,
			boolean[][] isPassed, String sourceCodeId) {
		Thread[] thread = new Thread[noTestSuite];
//		logger.info("sourceCodeId: " + sourceCodeId);

		for(int i=0; i<noTestSuite; i++){
			int testSuite = i+1;

			thread[i] = new Thread(new BugAnalysisThread(algorithm, totalPassed, totalFailed, testSuite, isPassed, sourceCodeId));
			thread[i].start();
		} //end for j
		
		for(int i=0; i<noTestSuite; i++){
			try {
				thread[i].join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} //end for j
	}
	
	private boolean[][] getTestResult(int numberOfTestSuite, int[] noTestCase) {
		
		int maxLength = getMaxValue(noTestCase);
		
		boolean[][] testResult = new boolean[numberOfTestSuite][maxLength];

		DBCursor trCursor = dbap.getDocumentsFrommTestResult();

		try{
			
			while(trCursor.hasNext()){
				DBObject trDBObj = trCursor.next();
				int testSuite = (int) trDBObj.get("test_suite");
				int testCase = (int) trDBObj.get("test_case");
								
				if((boolean)trDBObj.get("is_passed")) {
					testResult[testSuite-1][testCase-1] = true;
				} else {
					testResult[testSuite-1][testCase-1] = false;
				}
			}
			
		} finally {
			trCursor.close();
		}
		return testResult;

	}

	private int getMaxValue(int[] noTestCase) {
		
		int max=0;
		for(int i=0; i<noTestCase.length; i++){
			if(noTestCase[i] > max)
				max = noTestCase[i];
		}
		return max;
	}

	private int[] getTotalFailed(int[] totalPassed){
		int[] totalFailed = new int[totalPassed.length];
		TestConfigurator tConf = TestConfigurator.getInstance();

		for(int i=0; i<totalPassed.length; i++){
			int[] totalTestCase = tConf.getNumberOfTestCase();
			totalFailed[i] = totalTestCase[i] - totalPassed[i];
		}
		
		return totalFailed;
	}

	private int[] getTotalPassed(int numberOfTestSuite) {
		int[] totalPassed = new int[numberOfTestSuite];

		DBCursor trCursor = dbap.getDocumentsFrommTestResult();

		try{
			
			while(trCursor.hasNext()){
				DBObject trDBObj = trCursor.next();
				int testSuite = (int) trDBObj.get("test_suite");

				if((boolean)trDBObj.get("is_passed"))
					totalPassed[testSuite-1] = totalPassed[testSuite-1] + 1;
			}
			
		} finally {
			trCursor.close();
		}
		return totalPassed;
	}
	
	private void setTestResult(){
		TestSuite tSuite;
		TestCase tCase;
		
		if(tsList.size() != 0){
			for(int i=0; i<tsList.size(); i++){
				tSuite = tsList.get(i);
				
				for(int j=0; j<tSuite.size(); j++){
					tCase = tSuite.get(j);
					dbap.insertDataToTestResult(i+1, j+1, tCase.isPassed());
				}
			}
		} 
	}
	
	private void setSourceCodeLocation(String location) {
		CodeLoader cLoader = new JavaCodeLoader();
		cLoader.writeToDatabase(location);
	}
	
	private void setProjectName(String name) {
		// add the validation code with regard to the given name
		dbap.deleteDataBase(name);
		dbap.createDataBase(name);
		
		dbap.createSourceFile();
		dbap.createSourceCode();
		dbap.createCoverage();
		dbap.createSuspiciousness();
		dbap.createTestResult();
	}
	
	
	public void endDebug(){
		dbap.close();
	}
	
	private void setCoverage() {
//		logger.info("Inserting coverage data.....");			
		System.out.print("Inserting coverage data.....");
		insertCoverageToDatabase(tsList);
		System.out.println("ok");
//		logger.info("ok");
	} // end of setCoverage();

	private void insertCoverageToDatabase(List<TestSuite> tsList) {
		
		Thread[] thread = new Thread[tsList.size()];
		if(tsList.size() > 1){
			for(int i=0; i<tsList.size(); i++){
				int testSuiteNumber = i+1;
				TestSuite ts = tsList.get(i);
				thread[i] = new Thread(new CoverageWriterThread(testSuiteNumber, ts));
				thread[i].start();
			}
			
			for(int i=0; i<tsList.size(); i++){
				try {
					thread[i].join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} //end for j
		} else {
			TestSuite ts = tsList.get(0);
			CoverageWriterThread cwt = new CoverageWriterThread(1, ts);
			cwt.run();
		}
	}

}
